/**
 * @file simple_log_c.h
 * @author liuhc (liuhclhc@163.com)
 * @brief 纯C语言实现的简单日志库，支持自定义输出回调和时间戳格式化函数。
 *        支持内核态和用户态使用，提供不同日志级别和选项配置。
 * @version 0.1
 * @date 2025-09-23
 *
 * @copyright Copyright (c) 2025
 *
 */

#ifndef __SIMPLE_LOG_C_H__
#define __SIMPLE_LOG_C_H__

#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#ifdef __KERNEL__
#include <linux/string.h>
#include <linux/printk.h>
#else
#include <stdio.h>
#endif

typedef enum { LOG_NONE = 0, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG } LogLevel;

#define LOG_OPTION_SHOW_FILENAME (1 << 0)
#define LOG_OPTION_SHOW_FUNCNAME (1 << 1)
#define LOG_OPTION_SHOW_LINE (1 << 2)
#define LOG_OPTION_SHOW_TIMESTAMP (1 << 3)

#define LOG_OPTION_DEFAULT (LOG_OPTION_SHOW_TIMESTAMP)
#define LOG_OPTION_FULL                                                              \
    (LOG_OPTION_SHOW_TIMESTAMP | LOG_OPTION_SHOW_FILENAME | LOG_OPTION_SHOW_FUNCNAME \
     | LOG_OPTION_SHOW_LINE)
#define LOG_OPTION_FILE_LINE \
    (LOG_OPTION_SHOW_TIMESTAMP | LOG_OPTION_SHOW_FILENAME | LOG_OPTION_SHOW_LINE)

typedef int (*log_output_cb_t)(LogLevel level, const char* message, size_t msg_len);
typedef int (*log_timestamp_cb_t)(char* buffer, size_t buf_len);

static LogLevel           g_log_level    = LOG_DEBUG;
static int                g_log_options  = LOG_OPTION_DEFAULT;
static log_output_cb_t    g_output_cb    = NULL;
static log_timestamp_cb_t g_timestamp_cb = NULL;

static void log_format_and_output(LogLevel level, const char* file, const char* func, int line,
                                  const char* format, va_list args);

static size_t log_strlen(const char* str)
{
#ifdef __KERNEL__
    return strlen(str);
#else
    size_t len = 0;
    if (!str)
        return 0;
    while (str[len] != '\0' && len < SIZE_MAX - 1)
        len++;
    return len;
#endif
}

static char* log_strcpy(char* dest, const char* src, size_t dest_max_len)
{
#ifdef __KERNEL__
    return strlcpy(dest, src, dest_max_len);
#else
    if (!dest || !src || dest_max_len == 0)
        return dest;
    size_t i = 0;
    while (src[i] != '\0' && i < dest_max_len - 1)
    {
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0';
    return dest;
#endif
}

static char* log_strcat(char* dest, const char* src, size_t dest_max_len)
{
#ifdef __KERNEL__
    return strlcat(dest, src, dest_max_len);
#else
    if (!dest || !src || dest_max_len == 0)
        return dest;
    size_t dest_len = log_strlen(dest);
    if (dest_len >= dest_max_len - 1)
        return dest;
    return log_strcpy(dest + dest_len, src, dest_max_len - dest_len);
#endif
}

static char* log_itoa(int64_t num, char* buf, size_t buf_len)
{
    if (!buf || buf_len < 2)
        return NULL;
    char* ptr = buf;

    if (num < 0)
    {
        *ptr++ = '-';
        num    = -num;
    }

    if (num == 0)
    {
        *ptr++ = '0';
        *ptr   = '\0';
        return buf;
    }

    char   temp[32] = { 0 };
    size_t temp_idx = 0;
    while (num > 0 && temp_idx < sizeof(temp) - 1)
    {
        temp[temp_idx++] = '0' + (num % 10);
        num /= 10;
    }

    while (temp_idx > 0 && ptr < buf + buf_len - 1)
    {
        *ptr++ = temp[--temp_idx];
    }
    *ptr = '\0';
    return buf;
}

static const char* log_basename(const char* path)
{
    if (!path)
        return "(null)";
    const char* last_sep = NULL;
    const char* p        = path;
    while (*p != '\0')
    {
        if (*p == '/' || *p == '\\')
            last_sep = p;
        p++;
    }
    return last_sep ? last_sep + 1 : path;
}

static int log_default_output(LogLevel level, const char* msg, size_t msg_len)
{
#ifdef __KERNEL__
    static const char* klevel_prefix[] = {
        "",            // LOG_NONE
        KERN_ERR,      // LOG_ERROR
        KERN_WARNING,  // LOG_WARN
        KERN_INFO,     // LOG_INFO
        KERN_DEBUG     // LOG_DEBUG
    };

    if (level < LOG_NONE || level > LOG_DEBUG)
        return -1;

    char buf[512 + 32];
    snprintf(buf, sizeof(buf), "%s%s", klevel_prefix[level], msg);
    printk("%s", buf);
    return log_strlen(buf);
#else
    (void)level;
    if (msg && msg_len > 0)
        return fwrite(msg, 1, msg_len, stdout);
    return -1;
#endif
}

static void log_output(LogLevel level, const char* file, const char* func, int line,
                       const char* format, ...)
{
    if (level > g_log_level || level == LOG_NONE)
        return;

    va_list args;
    va_start(args, format);
    log_format_and_output(level, file, func, line, format, args);
    va_end(args);
}

// 函数实现（放在调用它的函数之后）
static void log_format_and_output(LogLevel level, const char* file, const char* func, int line,
                                  const char* format, va_list args)
{
    char prefix[128]   = { 0 };
    char timestamp[32] = { 0 };

    if (g_timestamp_cb)
    {
        int ts_len = g_timestamp_cb(timestamp, sizeof(timestamp));
        if (ts_len > 0 && (g_log_options & LOG_OPTION_SHOW_TIMESTAMP))
        {
            log_strcat(prefix, "[", sizeof(prefix));
            log_strcat(prefix, timestamp, sizeof(prefix));
            log_strcat(prefix, "] ", sizeof(prefix));
        }
    }

    const char* level_str[] = { "NONE", "ERROR", "WARN", "INFO", "DEBUG" };
    log_strcat(prefix, "[", sizeof(prefix));
    log_strcat(prefix, level_str[level], sizeof(prefix));
    log_strcat(prefix, "] ", sizeof(prefix));

    if (g_log_options & LOG_OPTION_SHOW_FILENAME)
    {
        const char* filename = file ? log_basename(file) : "(null)";
        log_strcat(prefix, "[", sizeof(prefix));
        log_strcat(prefix, filename, sizeof(prefix));
        log_strcat(prefix, "] ", sizeof(prefix));
    }

    if (g_log_options & LOG_OPTION_SHOW_FUNCNAME)
    {
        const char* funcname = func ? func : "(null)";
        log_strcat(prefix, "[", sizeof(prefix));
        log_strcat(prefix, funcname, sizeof(prefix));
        log_strcat(prefix, "] ", sizeof(prefix));
    }

    if (g_log_options & LOG_OPTION_SHOW_LINE)
    {
        char line_buf[16] = { 0 };
        log_itoa(line, line_buf, sizeof(line_buf));
        log_strcat(prefix, "[Line:", sizeof(prefix));
        log_strcat(prefix, line_buf, sizeof(prefix));
        log_strcat(prefix, "] ", sizeof(prefix));
    }

    char content[256] = { 0 };
#ifdef __KERNEL__
    extern int vscnprintf(char* buf, size_t size, const char* fmt, va_list args);
    vscnprintf(content, sizeof(content), format, args);
#else
    vsnprintf(content, sizeof(content), format, args);
#endif

    char full_log[512] = { 0 };
    log_strcat(full_log, prefix, sizeof(full_log));
    log_strcat(full_log, content, sizeof(full_log));
    log_strcat(full_log, "\n", sizeof(full_log));

    size_t full_len = log_strlen(full_log);

    if (g_output_cb)
        g_output_cb(level, full_log, full_len);
    else
        log_default_output(level, full_log, full_len);
}

static inline void log_register_output(log_output_cb_t cb)
{
    g_output_cb = cb;
}

static inline void log_register_timestamp(log_timestamp_cb_t cb)
{
#ifdef __KERNEL__
    g_timestamp_cb = cb;
#else
    g_timestamp_cb = cb;
#endif
}

static inline void log_set_level(LogLevel level)
{
    g_log_level = level;
}

static inline void log_set_option(int option, int enable)
{
    if (enable)
        g_log_options |= option;
    else
        g_log_options &= ~option;
}

#define LOG_ERROR(format, ...) \
    log_output(LOG_ERROR, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)

#define LOG_WARN(format, ...) \
    log_output(LOG_WARN, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)

#define LOG_INFO(format, ...) \
    log_output(LOG_INFO, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)

#define LOG_DEBUG(format, ...) \
    log_output(LOG_DEBUG, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)

#define LOG_ERROR_IF(cond, format, ...)       \
    do                                        \
    {                                         \
        if (cond)                             \
            LOG_ERROR(format, ##__VA_ARGS__); \
    } while (0)

#define LOG_WARN_IF(cond, format, ...)       \
    do                                       \
    {                                        \
        if (cond)                            \
            LOG_WARN(format, ##__VA_ARGS__); \
    } while (0)

#define LOG_INFO_IF(cond, format, ...)       \
    do                                       \
    {                                        \
        if (cond)                            \
            LOG_INFO(format, ##__VA_ARGS__); \
    } while (0)

#define LOG_DEBUG_IF(cond, format, ...)       \
    do                                        \
    {                                         \
        if (cond)                             \
            LOG_DEBUG(format, ##__VA_ARGS__); \
    } while (0)

#endif  // SIMPLE_LOG_H
